package edu.uj.petri.document;

import edu.uj.petri.gui.MainWindowInterface;
import edu.uj.petri.model.Model;
import edu.uj.petri.controller.EventHandler;
import edu.uj.petri.simulation.Simulation;
import edu.uj.petri.view.Canvas;
import edu.uj.petri.view.StandardView;
import edu.uj.petri.view.View;
import edu.uj.petri.commands.Command;
import edu.uj.petri.commands.CommandException;
import edu.uj.petri.common.Settings;
import edu.uj.util.Observer;

/**
 * stores all data connected with single diagram.
 *
 * <p><strong>Important:</strong> always call setMode before using this 
 * object.</p>
 * @author Piotrek
 */
public class Document implements Observer {
    
    private static Document activeDocument=null;
    
    private Model model;
    
    private String name;
    
    private Mode mode;
    private DesignMode designMode;
    private SimulationMode simulationMode;
    
    private EventHandler eventHandler;
    
    private View view;
    
    private Simulation simulation;
    
    private boolean changed;
    
    /**
     * default constructor
     */
    public Document(Canvas canvas) {
        this(canvas, Settings.getInstance().getModelFactory().createModel(), new StandardView());
    }
    
    /**
     * only to be used for creating documents read from file
     */
    public Document(Canvas canvas, Model model, View view) {
        // TODO INITIALIZE ALL FIELDS
        view.setCanvas(canvas);
        Settings settings = Settings.getInstance();
        this.model = model;
        this.view = view;
        simulation = settings.createSimulation();
        simulation.initialise(this);
        
        this.name = "untitled"; // TODO FIXME
        
        mode = null;
        designMode = new DesignMode();
        simulationMode = new SimulationMode();
        
        changed = false;
        
        model.subscribe(this);
        view.subscribe(this);
    }
    
    public void setCanvas(Canvas canvas)  {
        view.setCanvas( canvas );
    }
    
    public Canvas getCanvas()  {
        return view.getCanvas();
    }
    
    public String getName() {
        return name;
    }
    
    public void setName(String name) {
        this.name = name;
    }

    public void notifyFromObserved(int eventCode) {
        switch(eventCode) {
            case Model.CHANGED:
            case View.CHANGED:
                changed = true;
                break;
        }
    }
    
    /**
     * checks if any changes have been made to the document since last save
     */
    public boolean getChanged() {
        return changed;
    }
    
    public void undo() throws CommandException{
        mode.undo();
    }
    
    public void redo() throws CommandException {
        mode.redo();
    }
    
    public void execute(Command command) {
        mode.execute(command);
    }
    
    /**
     * document sets command stack of window and enables/disables its buttons.
     */
    public void setMode(DocumentModes mode, MainWindowInterface window) {
        if (this.mode != null)
            this.mode.finish(window, this);
        switch(mode) {
            case DESIGN:
                this.mode = designMode;
                break;
            case SIMULATION:
                this.mode = simulationMode;
                break;
        }
        this.mode.applyTo(window, this);
        view.draw();
    }
    
    /**
     * sets this document as active on main window. Document sets command
     * stack of window and enables/disables its buttons.
     */
    public void setActive(MainWindowInterface window) {
        this.mode.applyTo(window, this);
        Document.setActiveDocument(this);
        view.draw();
    }
    
    public Model getModel() {
        return model;
    }
    
    public View getView() {
        return view;
    }
    
    public Simulation getSimulation() {
        return simulation;
    }
    
    private static void setActiveDocument(Document document) {
        activeDocument = document;
    }
    
    /**
     * returns document currently edited/displayed in program
     */
    public static Document getActiveDocument() {
        return activeDocument;
    }
    
    /**
     * saves document to given saver. getChanged() should return false
     * after invoking this method.
     */
    public void save(DocumentSaver saver) throws Exception {
        saver.save(this);
        changed = false;
    }
}
